package edu.cs.usask.ca.silver.plugin.rcp.editors;

import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;

import edu.cs.usask.ca.silver.api.persistence.StorageI;
import edu.cs.usask.ca.silver.businesslogic.ids.BaseId;
import edu.cs.usask.ca.silver.businesslogic.ids.ParameterID;
import edu.cs.usask.ca.silver.businesslogic.ids.SilverIDVisitor;
import edu.cs.usask.ca.silver.plugin.rcp.dialogs.edit.EditModelObjectNameDialog;
import edu.cs.usask.ca.silver.plugin.rcp.jobs.LoggerReportScheduler;
import edu.cs.usask.ca.silver.plugin.rcp.views.projectviewer.ProjectViewI;

/**
 * The class that creates the appropriate editor pane determined by the ID type
 * the is contained in the tree viewer element selected. These editor panes are
 * divided into two section. The top part of the pane is generic to all
 * modelObject encountered within the RCP interface. The bottom part of each
 * pane contains specific data associated with the type of modelObject selected.
 * The ID is stored in the InfoEditorInput class variable modelObjectId
 * variable. This Id is used throughout the input editor to both obtain the
 * correct data for the database and to determined the required type of editors
 * pane needed.
 * 
 * @author Yudi Xue
 * 
 */
public class InfoEditor extends EditorPart implements IPropertyChangeListener {

	public static String ID = "edu.cs.usask.ca.silver.plugin.rcp.infoeditor";
	private String modelObjectID;
	private String modelObjectName;
	private final Font nameFont;
	private Label nameLabel;
	private Composite parent;
	private Composite compositeTopLevel;
	// private Text projectName;
	private String curDescription;
	private Text descriptionTextArea;
	private static int IS_VISIBLE = 1;
	private static Logger logger = Logger.getLogger(InfoEditor.class);

	/*
	 * this is her because it may have to be accessible to the support editor
	 * class
	 */
	ParameterID[] parameterIds;
	private StorageI storage;

	/**
	 * constructor for the class
	 */
	public InfoEditor() {
		nameFont = new Font(null, "Verdana", 18, 0);

	}

	@Override
	public void doSave(IProgressMonitor monitor) {
		saveDesc();
	}

	@Override
	public void doSaveAs() {
	}

	/**
	 * A method to initiated the editor pane
	 */
	@Override
	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		setSite(site);
		setInput(input);
		((InfoEditorInput) this.getEditorInput()).projectView
				.addPartPropertyListener(this);
		this.storage = ((InfoEditorInput) this.getEditorInput()).projectView
				.getAssociatedStorage();
	}/* end of init */

	/**
	 * A method to determine if the contents within an Editor pane has been
	 * edited and not saved before a editor is selected to be closed.
	 */
	@Override
	public boolean isDirty() {
		if (!(curDescription.equals(descriptionTextArea.getText()))) {
			return true;
		} else {
			return false;
		}
	}/* end of is dirty */

	@Override
	public boolean isSaveAsAllowed() {
		return false;
	}

	/**
	 * A method to create the main editor pane and to added the generic data
	 * required for the double clicked element from the structured tree viewer
	 * in the Project viewer. This data includes modelObject name,
	 * creator,creation date, and description.
	 * 
	 * @author Mike Taylor
	 */
	@Override
	public void createPartControl(Composite parent) {
		if (this.parent == null) {
			this.parent = parent;
		}
		setPartName(getObjectName());
		setTitleImage(((InfoEditorInput) getEditorInput()).getImageDescriptor()
				.createImage());

		compositeTopLevel = new Composite(parent, SWT.EMBEDDED | SWT.FILL);
		// compositeTopLevel = new Composite(parent, SWT.FILL);
		GridLayout layout = new GridLayout(3, true);
		compositeTopLevel.setLayout(layout);

		nameLabel = new Label(compositeTopLevel, SWT.LEFT);
		nameLabel.setFont(nameFont);
		nameLabel.setText(getObjectName());
		nameLabel.setLayoutData(new GridData());

		// Edit project name button
		// TODO: implement edit model metadata information
		Button editName = new Button(compositeTopLevel, SWT.END);
		editName.setText("Edit Name");
		editName.setLayoutData(new GridData(GridData.END, GridData.FILL, true,
				false, 2, 1));

		editName.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				EditModelObjectNameDialog d = new EditModelObjectNameDialog(
						compositeTopLevel.getShell(), getObjectName());
				int code = d.open();
				if (code == Window.OK) {
					try {
						getObjectId().setEntryNameToDatabase(
								d.getModelObjectName(), storage);
					} catch (Exception e1) {
						LoggerReportScheduler.scheduleErrorLogging(logger,
								"Failed to obtain entry name before editing name in infoeditor",
								e1);
					}
					setObjectName(d.getModelObjectName());

					getAssociatedProjectView().updateTree();
				}
			}
		});

		// //////////////////////////////////////////////////////////

		// Project Information group
		Group projectInfoGroup = new Group(compositeTopLevel,
				SWT.SHADOW_ETCHED_IN);
		GridData gridData = new GridData();
		gridData.horizontalSpan = 3;
		gridData.horizontalAlignment = SWT.FILL;
		layout = new GridLayout(2, false);
		projectInfoGroup.setLayoutData(gridData);
		projectInfoGroup.setLayout(layout);
		projectInfoGroup.setText("Metadata");

		// Labels for Type, Author, Creation Date and Description of the Model
		Label typeLabel = new Label(projectInfoGroup, SWT.LEFT);
		typeLabel.setText("Model Type: ");
		Label typeDataLabel = new Label(projectInfoGroup, SWT.LEFT);
		try {
			typeDataLabel.setText(getObjectId().getTypeFromDatabase(storage));
		} catch (Exception e1) {
			LoggerReportScheduler.scheduleErrorLogging(logger,
					"Failed to get simulation type from storage while add label for infoeditor",
					e1);
		}

		Label creatorLabel = new Label(projectInfoGroup, SWT.LEFT);
		creatorLabel.setText("Created by : ");

		Label creatorDataLabel = new Label(projectInfoGroup, SWT.LEFT);
		try {
			creatorDataLabel.setText(getObjectId().getCreatorNameFromDatabase(
					storage));
		} catch (Exception e1) {
			LoggerReportScheduler.scheduleErrorLogging(logger,
					"Failed to get creator name from storage while add label for infoeditor",
					e1);
		}

		Label creationDateLabel = new Label(projectInfoGroup, SWT.LEFT);
		creationDateLabel.setText("Created at : ");

		Label creationDateDataLabel = new Label(projectInfoGroup, SWT.LEFT);
		try {
			creationDateDataLabel.setText(getObjectId()
					.getCreationTime(storage).toString());
		} catch (Exception e1) {
			LoggerReportScheduler.scheduleErrorLogging(logger,"Failed to get creation time from storage while add label for infoeditor", e1);
		}

		Label descriptionLabel = new Label(projectInfoGroup, SWT.LEFT);
		descriptionLabel.setText("Description: ");

		descriptionTextArea = new Text(projectInfoGroup, SWT.BORDER | SWT.WRAP);
		descriptionTextArea.setEditable(true);
		try {
			descriptionTextArea.setText(getObjectId()
					.getDescriptionFromDatabase(storage));
		} catch (Exception e1) {
			LoggerReportScheduler.scheduleErrorLogging(logger,"Failed to get description type from storage while add label for infoeditor", e1);
			}
		GridData descGridData = new GridData(GridData.FILL, GridData.FILL,
				true, false, 3, 1);
		descGridData.heightHint = descriptionTextArea.getLineHeight() * 7;
		descriptionTextArea.setLayoutData(descGridData);
		curDescription = descriptionTextArea.getText();

		Button saveDescription = new Button(projectInfoGroup, SWT.CENTER);
		saveDescription.setText("Save Changes");
		saveDescription.setToolTipText("Save changes to description");
		saveDescription.setLayoutData(new GridData(GridData.END,
				GridData.CENTER, false, false, 3, 1));

		saveDescription.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				saveDesc();
			}
		});

		// Visitor pattern to create gui panel
		BaseId baseIdObject = getObjectId();
		SilverIDVisitor v = new InfoEditorCreatePanelSilverIDVisitorImpl(
				compositeTopLevel, this);
		try {
			baseIdObject.accept(v);
		} catch (Exception e1) {
			LoggerReportScheduler.scheduleErrorLogging(logger,"Failed to create visitor for silver id objects", e1);
		}

	}

	/**
	 * setFocus is critical in InfoEditor to gain focus when double click is
	 * performed. It will take focus from ProjectViewer to Editor, thus allows
	 * Selection Listener to be re-invoked when user click on items in
	 * ProjectViewer again.
	 */
	@Override
	public void setFocus() {
		compositeTopLevel.setFocus();
	}

	/**
	 * A method that returns the name of the object stored in the info input
	 * editor. This method is not used at this time. The name is coming directly
	 * from the database when required.
	 * 
	 * @return String object name associated with id store in this info input
	 *         editor
	 */
	private String getObjectName() {
		return ((InfoEditorInput) getEditorInput()).getModelObjectName();
	}/* end of get name */

	/**
	 * A method that sets the name of the object stored in the info input
	 * editor. This method is not used at this time.
	 * 
	 * @param newObjectName
	 *            string for new name
	 */
	private void setObjectName(String newObjectName) {
		modelObjectName = newObjectName;
		((InfoEditorInput) getEditorInput()).setName(newObjectName);
		setPartName(getObjectName());
		nameLabel.setText(newObjectName);
	}/* end of set name */

	/**
	 * A method the retrieves the Id that is store in the current Info editor
	 * input. This id is used through this class to determine the type of model
	 * object selected and to retrieve the appropriate data from the data base
	 * 
	 * @return BaseId associated with the currently selected element from the
	 *         structured tree viewer.
	 */
	protected BaseId getObjectId() {
		return ((InfoEditorInput) getEditorInput()).getModelObjectID();
	}/* end of get id */

	/*
	 * Performs the save action to description field in editor pane
	 */
	private void saveDesc() {
		try {
			curDescription = descriptionTextArea.getText();

			getObjectId().setDescriptionToDatabase(
					descriptionTextArea.getText(), storage);
			logger.info("Description is saved");

		} catch (Exception e1) {
			MessageDialog mdialog = new MessageDialog(
					compositeTopLevel.getShell(), "Model Object Out of Sync",
					null, "Please refresh this object, it is out of sync.", 0,
					null, 0);
			mdialog.open();
			LoggerReportScheduler.scheduleErrorLogging(logger,"Model Object Out of Sync!", e1);
		}
	}

	public final StorageI getAssociatedStorage() {
		return this.storage;
	}

	public final ProjectViewI getAssociatedProjectView() {
		return ((InfoEditorInput) this.getEditorInput()).projectView;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse
	 * .jface.util.PropertyChangeEvent)
	 */
	@Override
	public void propertyChange(PropertyChangeEvent event) {
	}

}/* end of class */
